Skip to content

Method: createNewInstance(Class, int)

1: /**
2: * Copyright (C) 2023 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.sessions;
16:
17: import cz.cvut.kbss.jopa.adapters.IndirectCollection;
18: import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
19: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
20:
21: import java.lang.reflect.Constructor;
22: import java.lang.reflect.Field;
23: import java.lang.reflect.InvocationTargetException;
24: import java.security.AccessController;
25: import java.security.PrivilegedActionException;
26: import java.util.Collection;
27: import java.util.Collections;
28: import java.util.HashMap;
29: import java.util.Map;
30: import java.util.Map.Entry;
31:
32: class MapInstanceBuilder extends AbstractInstanceBuilder {
33:
34: private static final Class<?> singletonMapClass = Collections.singletonMap(null, null)
35: .getClass();
36:
37: MapInstanceBuilder(CloneBuilderImpl builder, UnitOfWorkImpl uow) {
38: super(builder, uow);
39: }
40:
41: @Override
42: Object buildClone(Object cloneOwner, Field field, Object original, CloneConfiguration configuration) {
43: Map<?, ?> orig = (Map<?, ?>) original;
44: if (original instanceof IndirectCollection) {
45: orig = ((IndirectCollection<Map<?, ?>>) original).unwrap();
46: }
47: if (orig == Collections.emptyMap()) {
48: return orig;
49: }
50: final Class<?> origCls = orig.getClass();
51: Map<?, ?> clone;
52: clone = cloneUsingDefaultConstructor(cloneOwner, field, origCls, orig, configuration);
53: if (clone == null) {
54: if (singletonMapClass.isInstance(orig)) {
55: clone = buildSingletonClone(cloneOwner, field, orig, configuration);
56: } else if (Collections.emptyMap().equals(orig)) {
57: clone = orig;
58: }
59: else {
60: throw new IllegalArgumentException("Unsupported map type " + origCls);
61: }
62: }
63: clone = (Map<?, ?>) uow.createIndirectCollection(clone, cloneOwner, field);
64: return clone;
65:
66: }
67:
68: private Map<?, ?> cloneUsingDefaultConstructor(Object cloneOwner, Field field, Class<?> origCls, Map<?, ?> original,
69: CloneConfiguration configuration) {
70: Map<?, ?> result = createNewInstance(origCls, original.size());
71: if (result != null) {
72: cloneMapContent(cloneOwner, field, original, result, configuration);
73: }
74: return result;
75: }
76:
77: private static Map<?, ?> createNewInstance(Class<?> type, int size) {
78: Map<?, ?> result = null;
79: final Class<?>[] types = {int.class};
80: Object[] params;
81: Constructor<?> c = getDeclaredConstructorFor(type, types);
82:• if (c != null) {
83: params = new Object[1];
84: params[0] = size;
85: } else {
86: c = getDeclaredConstructorFor(type, null);
87: params = null;
88: }
89:• if (c == null) {
90: return null;
91: }
92: try {
93: result = (Map<?, ?>) c.newInstance(params);
94: } catch (InstantiationException | IllegalArgumentException | InvocationTargetException e) {
95: throw new OWLPersistenceException(e);
96: } catch (IllegalAccessException e) {
97: logConstructorAccessException(c, e);
98: try {
99: result = (Map<?, ?>) AccessController
100: .doPrivileged(new PrivilegedInstanceCreator(c));
101: } catch (PrivilegedActionException ex) {
102: logPrivilegedConstructorAccessException(c, ex);
103: // Do nothing
104: }
105: }
106: return result;
107: }
108:
109: private Map<?, ?> buildSingletonClone(Object cloneOwner, Field field, Map<?, ?> orig,
110: CloneConfiguration configuration) {
111: Entry<?, ?> e = orig.entrySet().iterator().next();
112: Object key = CloneBuilderImpl.isImmutable(e.getKey()) ? e.getKey() :
113: cloneObject(cloneOwner, field, e.getKey(), configuration);
114: Object value = CloneBuilderImpl.isImmutable(e.getValue()) ? e.getValue() :
115: cloneObject(cloneOwner, field, e.getValue(), configuration);
116: if ((value instanceof Collection || value instanceof Map) && !(value instanceof IndirectCollection)) {
117: value = uow.createIndirectCollection(value, cloneOwner, field);
118: }
119: return Collections.singletonMap(key, value);
120: }
121:
122: private void cloneMapContent(Object cloneOwner, Field field, Map<?, ?> source,
123: Map<?, ?> target, CloneConfiguration configuration) {
124: if (source.isEmpty()) {
125: return;
126: }
127: final Map<Object, Object> m = (Map<Object, Object>) target;
128: final Entry<?, ?> tmp = source.entrySet().iterator().next();
129: // Note: If we encounter null -> null mapping first, the whole map will be treated as immutable type map, which can be incorrect
130: final boolean keyPrimitive = CloneBuilderImpl.isImmutable(tmp.getKey());
131: final boolean valuePrimitive = CloneBuilderImpl.isImmutable(tmp.getValue());
132: for (Entry<?, ?> e : source.entrySet()) {
133: Object key;
134: Object value;
135: if (keyPrimitive) {
136: if (valuePrimitive) {
137: m.putAll(source);
138: break;
139: }
140: key = e.getKey();
141: value = cloneObject(cloneOwner, field, e.getValue(), configuration);
142: } else {
143: key = cloneObject(cloneOwner, field, e.getKey(), configuration);
144: value = valuePrimitive ? e.getValue() : cloneObject(cloneOwner, field, e.getValue(), configuration);
145: }
146: m.put(key, value);
147: }
148: }
149:
150: private Object cloneObject(Object owner, Field field, Object obj, CloneConfiguration configuration) {
151: Object clone;
152: if (obj == null) {
153: clone = null;
154: } else if (builder.isTypeManaged(obj.getClass())) {
155: clone = uow.registerExistingObject(obj, configuration.getDescriptor(), configuration.getPostRegister());
156: } else {
157: clone = builder.buildClone(owner, field, obj, configuration.getDescriptor());
158: }
159: return clone;
160: }
161:
162: @Override
163: void mergeChanges(Field field, Object target, Object originalValue, Object cloneValue) {
164: assert (originalValue == null) || (originalValue instanceof Map);
165: assert cloneValue instanceof Map;
166:
167: Map<Object, Object> orig = (Map<Object, Object>) originalValue;
168: Map<Object, Object> clone = (Map<Object, Object>) cloneValue;
169: if (clone instanceof IndirectCollection) {
170: clone = ((IndirectCollection<Map<Object, Object>>) clone).unwrap();
171: }
172: if (orig == null) {
173: orig = (Map<Object, Object>) createNewInstance(clone.getClass(), clone.size());
174: if (orig == null) {
175: orig = createDefaultMap(clone.size());
176: }
177: EntityPropertiesUtils.setFieldValue(field, target, orig);
178: }
179: orig.clear();
180: if (clone.isEmpty()) {
181: return;
182: }
183: for (Entry<?, ?> e : clone.entrySet()) {
184: final Object key = e.getKey();
185: final Object value = e.getValue();
186: final Object keyToPut = uow.contains(key) ? builder.getOriginal(key) : key;
187: final Object valueToPut = uow.contains(value) ? builder.getOriginal(value) : value;
188: orig.put(keyToPut, valueToPut);
189: }
190: }
191:
192: private static Map<Object, Object> createDefaultMap(int size) {
193: return new HashMap<>(size > 1 ? size : 16);
194: }
195:
196: @Override
197: boolean populatesAttributes() {
198: return true;
199: }
200: }